package com.unknow.first.dapp.admin.account.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lianziyou.chain.common.utils.ConvertContractValue;
import com.unknow.first.dapp.admin.account.constants.AccountConstants;
import com.unknow.first.dapp.admin.account.constants.ContractConstants;
import com.unknow.first.dapp.admin.account.mapper.AddressCoinConfig;
import com.unknow.first.dapp.admin.account.mapper.AddressRecharge;
import com.unknownman.block.constants.LogConstants;
import com.unknownman.block.entity.TContractLogs;
import com.unknownman.block.service.TContractLogsService;
import lombok.extern.slf4j.Slf4j;
import org.cloud.utils.SpringContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.web3j.abi.datatypes.Address;
import org.web3j.utils.Numeric;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;


@Component
@Slf4j
public class PayLogsService {

    @Autowired
    private TContractLogsService contractLogsService;

    @Autowired
    private DataSourceTransactionManager transactionManager;
    @Autowired
    private AddressCoinConfigService coinConfigService;
    @Autowired
    private AddressRechargeService rechargeService;


    /**
     * 处理充值入账
     */
    public void processPay(){
        //为了减少查询次数，此处未过滤事件的合约地址，如果有不同类型合约出现相同的事件topic0，那么需要过滤合约地址
        List<TContractLogs> logsList = contractLogsService.list(
                Wrappers.lambdaQuery(TContractLogs.class)
                        .eq(TContractLogs::getStatus, LogConstants.ContractLogStatus.UNTREATED.getStatus())
                        .eq(TContractLogs::getFirstTopic, ContractConstants.Event.PAY.topic0)
                        .orderByAsc(TContractLogs::getBlockTimestamp)
//                        .last("limit 100")
        );
        if (ObjectUtils.isEmpty(logsList)){
            return;
        }
        List<AddressCoinConfig> coinConfigList = coinConfigService.list(
                Wrappers.lambdaQuery(AddressCoinConfig.class)
                        .isNotNull(AddressCoinConfig::getPayContract)
                        .in(AddressCoinConfig::getStatus, AccountConstants.CoinConfigStatus.NORMAL.status, AccountConstants.CoinConfigStatus.ONLY_RECHARGE.status)
        );
        Map<String, AddressCoinConfig> coinConfigMap = coinConfigList.stream().collect(Collectors.toMap(_v -> _v.getPayContract().toLowerCase(), Function.identity()));
        for (TContractLogs contractLogs : logsList) {
            try {
                AddressCoinConfig coinConfig = coinConfigMap.get(contractLogs.getContractAddress().toLowerCase());
                if (coinConfig == null){
                    log.warn("充值合约未正确配置 - [logId:{}, payContract:{}]", contractLogs.getContractLogId(), contractLogs.getContractAddress());
                    continue;
                }
                savePayLogsToAccountDetail(contractLogs, coinConfig);
            } catch (Exception e) {
                log.error("{}, {}", e.getMessage(), e);
                e.printStackTrace();
            }
        }
    }


    /**
     * 扫描支付信息
     *
     * @throws Exception
     */
//    public void scanPayLog(Map<String, Object> params) throws Exception {
//        QueryWrapper<TContractLogs> contractLogsQueryWrapper = new QueryWrapper<>();
//
//        contractLogsQueryWrapper.ne("status", 100);
//        contractLogsQueryWrapper.eq("contract_address", params.get("payContract"));
//        contractLogsQueryWrapper.eq("first_topic", "0x357b676c439b9e49b4410f8eb8680bee4223724802d8e3fd422e1756f87b475f");
//        contractLogsQueryWrapper.last("limit 0,50");
//        List<TContractLogs> contractLogsList = contractLogsMapper.selectList(contractLogsQueryWrapper);
//
//        if (CollectionUtil.single().isEmpty(contractLogsList)) {
//            return;
//        }
//
//        contractLogsList.forEach(contractLogs -> {
//            savePayLogsToAccountDetail(params, contractLogs);
//        });
//    }

    void savePayLogsToAccountDetail(TContractLogs contractLogs, AddressCoinConfig coinConfig) {
        Assert.isTrue(contractLogs.getContractAddress().equalsIgnoreCase(coinConfig.getPayContract()), "事件和币种合约配置不匹配");
        //订单是否已处理
        boolean exist =rechargeService.getOne(Wrappers.lambdaQuery(AddressRecharge.class).eq(AddressRecharge::getLogsId, contractLogs.getContractLogId())) != null;
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            if (!exist) {
                //处理保存订单
                BigInteger amountBigInteger = Numeric.decodeQuantity(contractLogs.getThirdTopic());
                //发起充值地址
                String address = new Address(contractLogs.getSecondTopic()).getValue();
                //充值金额
                BigDecimal amount = ConvertContractValue.bigIntegerToBigDecimal(amountBigInteger, coinConfig.getCoinDecimals());
                String username = null;
                //充值处理器
                RechargeHandlerInterface rechargeHandlerInterface = SpringContextUtil.getBean(RechargeHandlerInterface.class);
                if (rechargeHandlerInterface != null) {
                    username = rechargeHandlerInterface.getUserName(address);
                }
                //充值记录
                AddressRecharge addressRecharge = AddressRecharge.builder()
                        .address(address)
                        .username(username)
                        .coinId(coinConfig.getCoinId())
                        .coinSymbol(coinConfig.getCoinSymbol())
                        .amount(amount)
                        .fee(BigDecimal.ZERO)
                        .realAmount(amount)
                        .logsId(contractLogs.getContractLogId())
                        .transactionHash(contractLogs.getTransHash())
                        .blockNumber(contractLogs.getBlockNumber())
                        .blockTimestamp(contractLogs.getBlockTimestamp())
                        .status(AccountConstants.RechargeStatus.CONFIRM_ING.value)
                        .createdBy("System")
                        .build();
                //保存充值记录
                int insertSelective = rechargeService.insertSelective(addressRecharge);
                Assert.isTrue(insertSelective > 0, "充值记录保存失败");
                //后置处理
                if (rechargeHandlerInterface != null){
                    rechargeHandlerInterface.doAfter(addressRecharge);
                }
                log.info("检测到新的充值 - [address:{}, coin:{}, hash:{}]", address, amount.toPlainString() + coinConfig.getCoinSymbol(), contractLogs.getTransHash());
            }
            //更新事件为已处理
            contractLogsService.updateStatusSuccess(contractLogs.getContractLogId());
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            log.error(e.getMessage(), e);
        }
    }


//    void savePayLogsToAccountDetail(Map<String, Object> params, TContractLogs contractLogs) {
//
//        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
//        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
//        TransactionStatus status = transactionManager.getTransaction(def);
//        try {
//            params.put("contractLogId", contractLogs.getContractLogId());
//            AddressAccountDetail addressAccountDetail = addressAccountDetailMapper.getPayAccountDetailByContractLogId(params);
//            JSONObject jsonObject = new JSONObject();
//            jsonObject.put(TradeRemarkColumn.PAY_BY.code, params.get("payContract"));
//            jsonObject.put("tokenContract", params.get("tokenContract"));
//            addressAccountDetail.setRemark(jsonObject.toJSONString());
//            BigInteger amountBigInteger = Numeric.decodeQuantity(contractLogs.getThirdTopic());
//            BigDecimal amount = ConvertContractValue.bigIntegerToBigDecimal(amountBigInteger,
//                    Integer.parseInt(params.getOrDefault("coinDecimals", 18).toString())); //从配置中获取代币精度，不存在就使用18为默认精度
//
//            addressAccountDetail.setAmount(amount);
//            addressAccountDetail.setReferId(contractLogs.getContractLogId());
//            addressAccountDetailMapper.insert(addressAccountDetail);
//
//            addressAccountInfoMapper.updateBalanceByAccountDetail(addressAccountDetail);
//            TContractLogs contractLogsUpdate = new TContractLogs();
//            contractLogsUpdate.setContractLogId(contractLogs.getContractLogId());
//            contractLogsUpdate.setStatus(100);
//            contractLogsMapper.updateById(contractLogsUpdate);
//            //充值后置处理器
//            RechargeHandlerInterface rechargeHandlerInterface = SpringContextUtil.getBean(RechargeHandlerInterface.class);
//            if (rechargeHandlerInterface != null){
//                rechargeHandlerInterface.doAfter(addressAccountDetail);
//            }
//            transactionManager.commit(status);
//        } catch (Exception e) {
//            log.error(e.getMessage(), e);
//            transactionManager.rollback(status);
//        }
//
//
//    }

}
